﻿namespace Knownet.BNF.Inline.Lang.CSharp
{
  using System;
  using System.IO;
  using System.Text;
  using System.Collections;
  using System.Collections.Generic;
  
  using Knownet.AST;
  using Knownet.AST.Source;
  using Knownet.BNF.Inline;

  /// <summary>
  /// 内联BNF打印机。
  /// </summary>
  /// <remarks>
  /// 这个打印机是一个手写打印机，
  /// 为了生成最基本的类定义，
  /// 所以我们实现这个打印机。
  /// 在c#的BNF定义生成之后，
  /// 请直接使用AstSourcePrinter
  /// 生成对应的代码，并避免使用
  /// 手写打印机。
  /// </remarks>
  public class CSharpInlinePrinter : AstSourcePrinter
  {
    #region Statics
    public static CSharpInlinePrinter of(List<BnfDefinition> definitions, TextWriter output)
    {
      return new CSharpInlinePrinter(definitions, output);
    }
    public static CSharpInlinePrinter of(List<BnfDefinition> definitions, TextWriter output, int tabSpaces)
    {
      return new CSharpInlinePrinter(definitions, output, tabSpaces);
    }
    #endregion
    #region Fields
    protected string accessModifier = "public";
    protected string staticModifier = "static";
    protected string containerClass = "CSharpDefinition";
    protected string nameSpace
      = "MoonPhase.BNF.Inline.Lang.CSharp";

    protected string baseClass
      = "BnfDefinition";

    protected string[] usings = new string[]
      {
        "System",
        "MoonPhase.AST",
        "MoonPhase.BNF.Inline"
      };
    protected List<BnfDefinition> definitions = null;
    #endregion
    #region Properties
    public virtual List<BnfDefinition> Definitions
    {
      get
      {
        return this.definitions;
      }
    }
    public virtual string BaseClass
    {
      get { return this.baseClass; }
      set { this.baseClass = value; }
    }
    public virtual string AccessModifier
    {
      get
      {
        return this.accessModifier;
      }
      set
      {
        this.accessModifier = (value == null) ? string.Empty : value;
      }
    }
    public virtual string StaticModifier
    {
      get
      {
        return this.staticModifier;
      }
      set
      {
        this.staticModifier = (value == null) ? string.Empty : value;
      }
    }
    public virtual string ContainerClass
    {
      get
      {
        return this.containerClass;
      }
      set
      {
        this.containerClass = (value == null) ? string.Empty : value;
      }
    }
    public virtual string Namespace
    {
      get
      {
        return this.nameSpace;
      }
      set
      {
        this.nameSpace = value;
      }
    }
    public virtual string[] Usings
    {
      get
      {
        return this.usings;
      }
      set
      {
        this.usings = value;
      }
    }

    #endregion
    #region Constructors
    public CSharpInlinePrinter(List<BnfDefinition> definitions, TextWriter output) : this(definitions, output, DefaultTabs) { }
    public CSharpInlinePrinter(List<BnfDefinition> definitions, TextWriter output, int tabSpaces)
    {
      if (definitions == null) throw new ArgumentNullException("definitions", "definitions can not be null!");
      if (output == null) throw new ArgumentNullException("writer", "writer can not be null!");
      this.definitions = definitions;
      this.output = output;
      this.TabSpaces = tabSpaces;
    }
    #endregion
    #region Methods
    public virtual void PrintDefinitionFile()
    {
      this.PrintNamespace(this.Namespace);
      {
        this.IndentOpen_("{");
        {
          this.PrintUsings(this.Usings);
          this.PrintContainer(this.Definitions);

        }
        this.IndentClose("}");
      }
      this.Output.Flush();
    }
    #region Structures
    protected virtual void PrintUsings(string[] usings)
    {
      if (usings != null)
      {
        foreach (string us in usings)
        {
          this.PrintUsing(us);
          this.PrintLine();
        }
      }
      this.PrintLine();
    }

    protected virtual void PrintContainer(IList definition)
    {
      this.PrintClassHeader(this.ContainerClass, this.BaseClass);
      this.IndentOpen_("{");
      {
        foreach (BnfDefinition def in definitions)
        {
          this.PrintDefinition(def, this.ContainerClass);
        }
      }
      this.IndentClose("}");
    }

    protected virtual void PrintDefinition(BnfDefinition definition,string baseClass)
    {
      this.PrintClassHeader(definition.Signature, baseClass);
      this.IndentOpen_("{");
      {
        //this.PrintConstructor(definition, true);
        this.PrintDefinitionInstance(definition, definition.Signature, "Instance");
        //对于非终结符，应当建立适当的静态构造方法。
        if (definition.HasContainingNodes)
        {
          if (definition.IsTerminalEquivalent)
          {

          }
          else if (definition.IsReferenceEquivalent)
          {

          }
          else
          {
            for (int i = 0; i < definition.ContainingNodes.Count; i++)
            {
              BnfClause clause = definition.ContainingNodes[i] as BnfClause;
              if (clause != null)
              {
                this.PrintClause(clause, i, definition.Signature, "of", definition.IsInductive);
              }
            }
          }
        }
      }
      this.IndentClose("}");
      this.PrintLine();
    }

    protected virtual void PrintClause(BnfClause clause,int clauseId,string className,string methodName,bool keepTerminal)
    {
      if (clause.HasContainingNodes)
      {
        ArrayList sparts = new ArrayList();
        ArrayList allparts = new ArrayList();

        for (int i = 0; i < clause.ContainingNodes.Count; i++)
        {
          string name = (clause.ContainingNodes[i] as SyntaxNode).Signature;
          allparts.Add(name);

          if (keepTerminal)
          {
            sparts.Add(name);
          }
          else if (!this.IsTerminalDefinition(name))
          {
            sparts.Add(name);
          }
        }
        if (sparts.Count > 0)
        {
          this.PrintMethodSignature(this.AccessModifier, this.StaticModifier, className, methodName, (string[])sparts.ToArray(typeof(string)));
          this.PrintLine();
          this.IndentOpen_("{");
          {
            this.Print("return");
            this.Print(" ");
            this.PrintNonSeparatingList(className, ".", "Instance", ".", "AcceptClause");

            this.Print("(");
            {
              this.Print(clauseId.ToString());
              foreach (string name in allparts)
              {
                this.PrintSeparator(",");
                if (sparts.Contains(name))
                {
                  //打印在spart列表中的项目为变量。
                  this.Print(this.ModifyNameOf(name));
                }
                else
                {
                  //未在列表中的，视作终结符，直接输入。
                  this.PrintNonSeparatingList(name, ".", "Instance");
                }
              }
            }
            this.Print(")");
            this.PrintSpaceSeparatingList("as", className);
            this.PrintStatementEnd();
            this.PrintLine();
          }
          this.IndentClose("}");
        }
      }
    }
    protected virtual void PrintDefinitionInstance(BnfDefinition definition, string className, string fieldname)
    {
      this.PrintSpaceSeparatingList(this.AccessModifier, this.StaticModifier, className, fieldname);
      this.PrintLine();

      this.IndentOpen_("{");
      {
        this.PrintLine("get");
        
        this.IndentOpen_("{");
        {
          this.PrintSpaceSeparatingList("return", "new", className);
          this.PrintNonSeparatingList( "()", ".", "DefineAs","(");
          this.PrintLine();

          this.Indent();
          {
            this.PrintDirectly(definition.ToString(this.IdentSpaces));
          }
          this.Unindent();

          this.PrintSpaceSeparatingList(")", "as", className);
          this.PrintStatementEnd();
          this.PrintLine();
        }
        this.IndentClose("}");
      }
      this.IndentClose("}");

    }


    protected virtual void PrintMethodSignature(string accessModifier,string staticModifer,string ret_type, string name, params string[] sparts)
    {
      this.PrintSpaceSeparatingList(accessModifier, staticModifer, ret_type, name);

      this.Print("(");
      if (sparts != null)
      {
        for (int i = 0; i < sparts.Length; i++)
        {
          string spart = sparts[i];

          this.PrintSpaceSeparatingList(spart,this.ModifyNameOf(spart));
          this.PrintSeparator(",", i != sparts.Length - 1);
        }
      }
      this.Print(")");
    }

    
    protected virtual void PrintClassHeader(string className, params string[] derives)
    {
      this.PrintSpaceSeparatingList(this.AccessModifier, "class", className);

      if (derives != null)
      {
        this.PrintListStart();
        this.PrintCommaSeparatingList(derives);
      }
      this.PrintLine();
    }

    protected virtual void PrintUsing(string us)
    {
      this.PrintSpaceSeparatingList("using", us);
      this.PrintStatementEnd();
    }
    protected virtual void PrintNamespace(string ns)
    {
      this.PrintSpaceSeparatingList("namespace", ns);
      this.PrintLine();
    }

    protected virtual void PrintStatementEnd()
    {
      this.PrintSeparator(";");
    }

    protected virtual void PrintListStart()
    {
      this.PrintSeparator(":");
    }

    protected virtual string ModifyNameOf(string spart)
    {
      return (spart != null) ? "_" + spart.ToLower() : string.Empty;
    }
    protected virtual bool IsTerminalDefinition(string name)
    {
      foreach (BnfDefinition def in this.Definitions)
      {
        if (def != null && def.Signature == name && def.IsTerminalEquivalent)
          return true;
      }
      return false;
    }
    protected virtual void PrintNonSeparatingList(params string[] list)
    {
      this.PrintList("", list);
    }
    protected virtual void PrintCommaSeparatingList(params string[] list)
    {
      this.PrintList(",", list);
    }
    protected virtual void PrintSpaceSeparatingList(params string[] list)
    {
      this.PrintList(" ", list);
    }
    protected virtual void PrintList(string separator, params string[] list)
    {
      if (list != null && list.Length > 0)
      {
        for (int i = 0; i < list.Length; i++)
        {
          this.Print(list[i]);
          this.PrintSeparator(separator, i != list.Length - 1);
        }
      }
    }

    #endregion
    #endregion
  }
}
